package stc.skymobi.netty.handler.codec.http.decode;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.FullHttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import stc.skymobi.bean.bytebean.core.BeanFieldCodec;
import stc.skymobi.bean.tlv.decode.TLVDecoderOfBean;
import stc.skymobi.bean.util.meta.Int2TypeMetainfo;
import stc.skymobi.bean.xip.core.SaipSignal;
import stc.skymobi.netty.util.NettyConstants;
import stc.skymobi.transport.protocol.xip.XipHeader;

import java.util.Arrays;
import java.util.List;

@ChannelHandler.Sharable
public class NettyMixedHttpDecoder extends MessageToMessageDecoder<FullHttpResponse> {
    private static final Logger logger = LoggerFactory.getLogger(NettyMixedHttpDecoder.class);

    private int dumpBytes = 256;

    private BeanFieldCodec saipBeanCodec;
    private Int2TypeMetainfo saipTypeMetainfo;

    private TLVDecoderOfBean tlvBeanDecoder;

    @Override
    public void decode(ChannelHandlerContext ctx, FullHttpResponse msg, List<Object> out) throws Exception {
        logger.debug("decode {}", msg);
        if (msg.getStatus().code() == 200) {
            ByteBuf content = msg.content();
            if (content.isReadable()) {
                byte[] bytes = new byte[content.readableBytes()];
                content.getBytes(0, bytes);
                logger.debug("resp hex:{}", ByteBufUtil.hexDump(content, 0, bytes.length > dumpBytes ? dumpBytes : bytes.length));

                Object resp;
                Class respType = ctx.channel().attr(NettyConstants.CTX_HTTP_RESP_CLASS).get();
                if (respType != null) {
                    resp = tlvBeanDecoder.decode(bytes.length, bytes,
                            tlvBeanDecoder.getDecodeContextFactory().createDecodeContext(respType, null));
                } else {
                    resp = decodeSaipSignal(bytes);
                }

                logger.debug("decoded resp:{}", resp);
                out.add(resp);
            }
        }
    }

    private SaipSignal decodeSaipSignal(byte[] bytes) throws Exception {
        XipHeader header = (XipHeader) saipBeanCodec.decode(
                saipBeanCodec.getDecContextFactory().createDecContext(
                        bytes, XipHeader.class, null, null)
        ).getValue();
        Class<?> type = saipTypeMetainfo.find(header.getMessageCode());
        if (null == type) {
            return null;
        }
        byte[] bodyBytes = Arrays.copyOfRange(bytes, XipHeader.SAIP_HEADER_LENGTH, bytes.length);
        SaipSignal signal = (SaipSignal) saipBeanCodec.decode(
                saipBeanCodec.getDecContextFactory().createDecContext(
                        bodyBytes, type, null, null)
        ).getValue();
        if (null != signal) {
            signal.setIdentification(header.getTransactionAsUUID());
            signal.setSrcModule(header.getSrcModule());
            signal.setDstModule(header.getDstModule());
        }

        return signal;
    }

    public void setSaipBeanCodec(BeanFieldCodec saipBeanCodec) {
        this.saipBeanCodec = saipBeanCodec;
    }

    public void setSaipTypeMetainfo(Int2TypeMetainfo saipTypeMetainfo) {
        this.saipTypeMetainfo = saipTypeMetainfo;
    }

    public void setTlvBeanDecoder(TLVDecoderOfBean tlvBeanDecoder) {
        this.tlvBeanDecoder = tlvBeanDecoder;
    }

    public int getDumpBytes() {
        return dumpBytes;
    }

    public void setDumpBytes(int dumpBytes) {
        this.dumpBytes = dumpBytes;
    }
}
